<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    //命名规范：普通函数名称，首字母小写，构造函数首字母大写
    function Person(name, age) {
        //添加属性和方法只能在函数内写
        //方法中如果需要使用属性和其它时，必须通过this
        this.name = name;
        this.age = age;
        this.hubby = ["java", "c++"];
        //如果构造函数中有方法，那就会造成内存的浪费
        // this.say = function () {
        //     // console.log(name);//不是使用属性name，而是使用形参name
        //     console.log(this.name);//使用的是属性name
        //     console.log(this.hubby);
        // }
    }

    /*
        原型
            原型对象(函数原型对象)：函数名.prototype
            对象原形：对象名.__proto__，指向的是创建该对象的构造函数原型
            在对象是由该构造函数创建时，对象名.__proto__ === 函数名.prototype。才有意义
     */

    //挂载在默认的原型对象上，默认原型对象的constructor属性指向构造函数本身
    Person.prototype.say = function () {
        console.log(this.name);//使用的是属性name
        console.log(this.hubby);
    }

    Person.prototype.money = 100;
    console.dir(Person.prototype);

    //自定义原型对象，constructor不会指向构造函数本身
    let student = {
        constructor: Person,
        study: function () {
            console.log("study");
        },
        game: function () {
            console.log("game");
        },
        //只能通过student.money修改值
        money: 1000
    }

    function Teacher() {
        this.constructor = Person;
        this.study = function () {
            console.log("study");
        }
        teach = function () {
            console.log("teach");
        }
        //给对象上添加了一个money，但无法修改原型上的money属性
        this.setMoney = function (money) {
            this.money = money;
        }
        //原始数据类型在原型上，无法修改
        this.money = 500//属性在原型对象上，不在对象本身
    }

    //分组方法，批量挂载到原型对象上
    let teacher = new Teacher();

        Person.prototype = teacher;
    // Person.prototype = student;

    /*
        原型对象，有一个属性constructor，本质上没有啥用，默认是指向构造该对象的构造函数。就算不指向构造函数也不会有什么影响，但是建议指向
     */

    // Object.prototype
    //构造函数创建的对象，默认执行深拷贝
    //但是对于函数类型的属性时，如果执行深拷贝就会造成内存的浪费，因为函数保存时没必要使用多个内存地址，
    let p1 = new Person("p1", 10);
    p1.hubby.push("javascript");
    // p1.say();
    p1.setMoney(1000);
    console.log(p1);
    let p2 = new Person("p2", 10);
    p2.hubby.push("android");
    // p2.say();
    console.log(p2);
    // p2.money = 700;//是在对象p2上，添加一个money属性，并不是修改原型上的值
    p2["money"] = 700;//是在对象p2上，添加一个money属性，并不是修改原型上的值
    p2.setMoney(1000);
    //方法和属性的查找，如果在对象本身上找到就返回，找不到就去原型对象上找，还找不到，就去原型对象上的原型对象上找，直接Object顶层，最后还没有，则返回null
    console.log(p2);
    //为什么p2会有hasOwnProperty()这个方法
    console.log(p2.hasOwnProperty("name"));
    console.log("-----------instanceof-------------")
    console.log(p2 instanceof Person);//true
    console.log(p2 instanceof Teacher);//true
    console.log(p2 instanceof Array);//false
    console.log(p2 instanceof Object);//true
    console.log("-----------isPrototypeOf-------------")
    console.log(Person.prototype.isPrototypeOf(p2));
    console.log(Teacher.prototype.isPrototypeOf(p2));
    console.log(Array.prototype.isPrototypeOf(p2));
    console.log(Object.prototype.isPrototypeOf(p2));


    console.log(p1.__proto__ === Person.prototype);
    console.log(p1.__proto__ === Array.prototype);

    // 练习，通过原型给Array添加一个sum()方法，计算数组的总和
    let a = [1, 3, 5, 7, 9];
    Array.prototype.sum = function () {
        // console.log(this);
        return this.reduce((pre, cur) => {
            return pre += cur;
        }, 0);
    }
    console.log(a.sum());
    console.log("-----------isPrototypeOf-------------")
    console.log(Person.prototype.isPrototypeOf(a));
    console.log(Array.prototype.isPrototypeOf(a));
    console.log(Object.prototype.isPrototypeOf(a));
</script>
</body>
</html>